package com.xinyue.game.utils.token;

import com.alibaba.fastjson.JSON;
import com.google.common.io.BaseEncoding;
import com.xinyue.game.utils.encrypt.AESUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;

/**
 * token管理，此token用于在中心服和游戏服之间的认证，这里token使用对称加密算法生成
 * 因为这个token只会在服务端解密，所以密钥是安全的
 *
 * @Author 王广帅
 * @Date 2021/5/10 23:55
 */
public class TokenManager<T extends BaseGameToken> {
    private static Logger logger = LoggerFactory.getLogger(TokenManager.class);
    private byte[] aesFactorBytes;

    public TokenManager(String aesKey) {
        if (StringUtils.isBlank(aesKey)) {
            throw new IllegalArgumentException("对称密钥不能为空");
        }
        if (aesKey.length() % 16 != 0) {
            throw new IllegalArgumentException("对称密钥的长度必须是16的倍数");
        }
        this.aesFactorBytes = aesKey.getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 创建token,对token中的消息进行对称加密
     *
     * @param tokenParam
     * @return
     * @throws Exception
     */
    public String createToken(BaseGameToken tokenParam) throws Exception {
        String value = JSON.toJSONString(tokenParam);
        byte[] tokenBytes = value.getBytes(StandardCharsets.UTF_8);
        byte[] encryptValue = AESUtils.encode(aesFactorBytes, tokenBytes);
        String token = BaseEncoding.base64().encode(encryptValue);
        return token;
    }

    /**
     * 解密token，从token中获取数据
     *
     * @param token
     * @return
     * @throws Exception
     */
    public T decodeToken(String token, Class<T> clazz) throws TokenDecodeException, TokenExpireException {
        if (StringUtils.isBlank(token)) {
            throw new TokenDecodeException("token不能为空");
        }
        try {
            T tokenObj = decryptToken(token, clazz);
            long nowTime = System.currentTimeMillis();
            if (nowTime > tokenObj.getEndTime()) {
                throw new TokenExpireException("token 已过期");
            }
            return tokenObj;
        } catch (TokenExpireException e) {
            logger.error("token已过期");
            throw e;
        } catch (Exception e) {
            logger.error("token错误，解析异常", e);
            throw new TokenDecodeException("token错误，解析异常");
        }
    }

    private T decryptToken(String token, Class<T> clazz) throws Exception {
        byte[] data = BaseEncoding.base64().decode(token);
        byte[] unEncryptValue = AESUtils.decode(aesFactorBytes, data);
        String json = new String(unEncryptValue, StandardCharsets.UTF_8);
        T tokenObj = JSON.parseObject(json, clazz);
        return tokenObj;
    }

}
